/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package controladores;

import ayuda.Ayuda;
import persistencia.DAO;
import persistencia.InputDAO;
import persistencia.OutputDAO;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

import tesauro.CargadorMasivoArchivoTexto;
import tesauro.DiccionarioSinonimos;
import tesauro.IHashCodeStrategy;
import tesauro.ILista;
import tesauro.IObserver;
import tesauro.ListaLexicografica;

/**
 *
 * @author demian
 */
public class ControladorGeneral implements IObserver {    
    private DiccionarioSinonimos diccionario;
    private IControladorObserver observer;

    public ControladorGeneral() {
        diccionario = new DiccionarioSinonimos();
        diccionario.setOberver(this);      
    }
    
    /**
     * Setea el observador para el controlador. El observador
     * podra saber cuando el controlador cambia al diccionario.
     * 
     * @param observer
     */
    public void setObserver(IControladorObserver observer) {
        this.observer = observer;
    }
    
    /**
     * Agrgea una palabra y sus sinonimos.
     * 
     * @param palabra palabra a agregar.
     * @param sinonimos lisa de sinonimos separados por coma.
     * @throws IOException 
     */
    public void agregarPalabra(String palabra, String sinonimos){
        String []splitted = sinonimos.split(",");
        ArrayList<String> sinonimosList = new ArrayList<String>(splitted.length);
        StringBuffer buffer = new StringBuffer();
        
        for(String sinonimo : splitted) {
            buffer.delete(0, buffer.length());
            buffer.append(sinonimo.trim());
            
            if(buffer.length() == 0) {
                continue;
            }
            
            sinonimosList.add(buffer.toString());
        }
        
        diccionario.add(palabra, sinonimosList);
    }

    /**
     * Agrega un sinonimo a una palabra. Si la palabra no existe
     * ninguna accion se lleva a cabo. Si el sinonimo ya existe para
     * la palabra ninguna accion se lleva a cabo.
     * 
     * @param palabra palabra a la cual agregarle el sinonimo.
     * @param sinonimo sinonimo a agregar.
     */
    public void agregarSinonimo(String palabra, String sinonimo) {
        if(!diccionario.contains(palabra)) {
            return;
        }
        
        diccionario.addSinonimo(palabra, sinonimo);
    }
    
    /**
     * Obtiene un string con informacion luego de hacer rehash.
     */
    public String obtenerResultadoReHash() {
        StringBuffer buffer = new StringBuffer();
        
        buffer.append("Factor de carga: " + diccionario.getFactorDeCarga());
        buffer.append("\nColisiones rehash: " + diccionario.getColisionesRehash());
        
        return buffer.toString();
    }
    
    /**
     * Carga un nuevo diccionario desde un archivo de texto.
     * 
     * @param archivo archivo.
     * @throws java.lang.Exception
     */
    public void cargarDesdeArchivo(File archivo) throws Exception {
        diccionario = new DiccionarioSinonimos();
        diccionario.setOberver(this);
        diccionario.cargarMasivamente(new CargadorMasivoArchivoTexto(archivo));
    }
    
    /**
     * Carga un diccionario desde un archivo de texto.
     * 
     * @param archivo archivo.
     * @throws java.lang.Exception
     */
    public void cargarDesdeArchivoSerializable() throws IOException, ClassNotFoundException {
        DAO serializable = new InputDAO();
        diccionario = (DiccionarioSinonimos)serializable.read();
        diccionario.setOberver(this);
    }
    
    /**
     * Cambia el algoritmo de hash usado por el diccionario. Se hace
     * un re hash del mismo cuando se cambia el algoritmo.
     * 
     * @param algoritmo nombre de la clase del algoritmo.
     * @throws java.lang.ClassNotFoundException si no existe la clase. 
     * @throws java.lang.Exception error no esperado.
     */
    public void cambiarAlgoritmoHash(String algoritmo) throws ClassNotFoundException,  Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        classLoader.loadClass(algoritmo);
        IHashCodeStrategy hashCodeStrategy = (IHashCodeStrategy)Class.forName(algoritmo).newInstance();
        diccionario.setFuncionHash(hashCodeStrategy);
    }

    /**
     * Chequea si una palaba ya esta cargada en el diccionario.
     * 
     * @param palabra palabra a chquear.
     * @return true si existe, false si no existe.
     */
    public boolean existePalabra(String palabra) {
        return diccionario.contains(palabra);
    }
    
    /**
     * Cheque si hay un diccionario cargado.
     * 
     * @return true si hay diccionario cargado.
     */
    public boolean hayDiccionarioCargado() {
        return (diccionario == null || diccionario.size() == 0) ? false : true;
    }
    
    /**
     * Remueve una palabra del diccionario.
     * 
     * @param palabra palabra a remover.
     * @throws java.lang.Exception si no hay diccionario cargado.
     */
    public void removerPalabra(String palabra) throws Exception {
        if(diccionario == null) {
            throw new Exception("El diccionario no esta cargado.");
        }
        
        diccionario.remove(palabra);
    }
    
    /**
     * Remueve un sinonimo de la palabra.
     * 
     * @param palabra palabra a la cual removerle el sinonimo.
     * @param sinonimo sinonimo a remover.
     * @throws java.lang.Exception si no hay diccionario cargado.
     */
    public void removerSinonimoDe(String palabra, String sinonimo) throws Exception {
        if(diccionario == null) {
            throw new Exception("El diccionario no esta cargado.");
        }

        diccionario.removeSinonimoDe(palabra, sinonimo);
    }
    
    /**
     * Obtiene la lista de sinonimos de una palabra.
     * 
     * @param palabra a la cual se le quieren obtener los sinonimos.
     * @return lista de sinonimos o null si no existe la palabra.
     * @throws java.lang.Exception si no hay diccionario cargado.
     */
    public List<String> obtenerSinonimosDe(String palabra) throws Exception {
        if(diccionario == null) {
            throw new Exception("El diccionario no esta cargado.");
        }
        
        return diccionario.getSinonimosDe(palabra);
    }

    /**
     * Obtiene la funcion hash seteada en el diccionario.
     * 
     * @return instancia de la funcion hash.
     * @throws java.lang.Exception si no hay diccionario cargado.
     */
    public IHashCodeStrategy obtenerAlgoritmoHash() throws Exception {
        if(diccionario == null) {
            throw new Exception("El diccionario no esta cargado.");
        }
        
        return diccionario.getFuncionHash();
    }
    
    /**
     * Obtiene el diccionario llistado en forma lexicograficamente.
     * 
     * @return instancia de un TreeMap que contendra las entradas del diccionario
     * ordenadas.
     * @throws java.lang.Exception si no hay diccionario cargado.
     */
    public TreeMap<String,List<String>> lista() throws Exception {
    	TreeMap<String,List<String>> treeMap = new TreeMap<String,List<String>>();

    	if(diccionario.estaVacio()) {
            throw new Exception("El diccionario no esta cargado.");
        }
        else
        {       	
        	ILista listado = new ListaLexicografica();
        	treeMap = (TreeMap<String,List<String>>) listado.sorted(diccionario);        	
        }      
    	
        return treeMap;
    }

    /**
     * Metodo de IObserver para observar al diccionario y 
     * que es llamado cada vez que el diccionario cambia.
     * 
     * @param diccionario instancia del diccionario con sus ultimos cambios.
     */
    public void update(final DiccionarioSinonimos diccionario) {
        if(observer != null) {
            StringBuffer buffer = new StringBuffer();

            if(diccionario.estaVacio()) {
                buffer.append("Diccionario vacio o no cargado.");  
            } else {
                buffer.append( "Palabras: " + diccionario.size() + " - Colisiones: " + diccionario.getColisiones() +  " -  Factor de Carga Tabla: " + diccionario.getFactorDeCarga());
            }
            observer.update(buffer.toString());
        }
    }
    
    /**
     * Guarda el diccionario en un archivo mediante
     * serilizacion.
     */
    public void guardarEnSerializable(){
    	try{
        	DAO serializable = new OutputDAO();
        	serializable.write(diccionario);
        }catch(IOException ioe){
        	new IOException("Error al guardar el archivo serializado. " + ioe.getMessage());
        }
    }
}
